/*
 * QTSM LIB API Implement
 *
 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <string.h>

#include "qtsm_lib.h"
#include "qtsm_cbor_msg.h"

#define QTSM_REQUEST_MAX_SIZE   0x1000
#define QTSM_RESPONSE_MAX_SIZE  0x6000

/**
 * QTSM_DEV_NAME - Zero Enclaves misc device that provides the ioctl interface.
 */
#define QTSM_DEV_NAME           "/dev/qtsm"

#define QTSM_IOCTL_MAGIC        0xC7
#define QTSM_IO_REQUEST         _IOWR(QTSM_IOCTL_MAGIC, 0, struct qtsm_message)

struct qtsm_message {
    /* Request from user */
    struct iovec request;
    /* Response to user */
    struct iovec response;
};

int qtsm_lib_init(void)
{
    int qtsm_dev_fd;

    qtsm_dev_fd = open(QTSM_DEV_NAME, O_RDWR | O_CLOEXEC);
    if (qtsm_dev_fd < 0) {
        printf("QTSM Device file failed to open.\n");
        return -1;
    }

    printf("QTSM Device file [%d] opened successfully.\n", qtsm_dev_fd);
    return qtsm_dev_fd;
}

void qtsm_lib_exit(int qtsm_dev_fd)
{
    int rc;
    rc = close(qtsm_dev_fd);
    if (!rc)
        printf("File of descriptor [%d] closed successfully.\n", qtsm_dev_fd);
    else
        printf("File of descriptor [%d] failed to close.\n", qtsm_dev_fd);
}

static int qtsm_ioctl(int qtsm_dev_fd, const struct qtsm_message *message)
{
    int rc;

    rc = ioctl(qtsm_dev_fd, QTSM_IO_REQUEST, message);
    if (rc < 0) {
        printf("Error in handle qtsm message [%m]\n");
        return rc;
    }

    return 0;
}

static int qtsm_process_request(int qtsm_dev_fd,
    const struct qtsm_message *message)
{
    int rc;
    rc = qtsm_ioctl(qtsm_dev_fd, message);
    if (rc < 0) {
        fprintf (stderr, "Error on qtsm_ioctl [%m]\n");
        return rc;
    }
    return rc;
}

static int qtsm_encode_request_cbor(const struct qtsm_message_req *req,
    unsigned char **request, size_t *request_size)
{
    cbor_item_t *root = NULL;
    size_t length;
    unsigned char *buf = NULL;
    int rc;

    root = cbor_new_indefinite_array();
    if (!root) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    buf = malloc(QTSM_REQUEST_MAX_SIZE);
    if (!buf) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    if (!cbor_qtsm_message_req(root, req)) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    length = cbor_serialize(root, buf, QTSM_REQUEST_MAX_SIZE);
    if (length == 0) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    *request_size = length;
    *request = malloc(*request_size);
    if (!(*request)) {
        rc = INTERNAL_ERROR;
        goto error;
    }
    memcpy(*request, buf, *request_size);

    rc = NO_ERROR;

error:
    if (root)
        cbor_decref(&root);
    if (buf)
        free(buf);
    return rc;
}

static int qtsm_decode_response_cbor(const char *response,
    unsigned int response_size, struct qtsm_message_res *res)
{
    cbor_item_t *root = NULL;
    struct cbor_load_result result;
    int rc;

    root = cbor_load(response, response_size, &result);
    if (result.error.code != CBOR_ERR_NONE) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    if (!cbor_qtsm_message_res(root, res)) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    rc = NO_ERROR;

error:
    if (root)
        cbor_decref(&root);
    return rc;
}

static int qtsm_message_alloc(struct iovec *res)
{
    void *res_msgbuf = NULL;

    if (res->iov_len == 0 ||
        res->iov_len > QTSM_RESPONSE_MAX_SIZE)
        return INTERNAL_ERROR;

    res_msgbuf = malloc(res->iov_len);
    if (!res_msgbuf) {
        fprintf(stderr, "failed to malloc res_msgbuf.\n");
        return INTERNAL_ERROR;
    }

    memset(res_msgbuf, 0, res->iov_len);
    res->iov_base = res_msgbuf;

    return NO_ERROR;
}

static void qtsm_message_free(struct iovec *req, struct iovec *res)
{
    if (req && req->iov_base) {
        free(req->iov_base);
        req->iov_base = NULL;
    }

    if (res && res->iov_base) {
        free(res->iov_base);
        res->iov_base = NULL;
    }
}

static void qtsm_response_free(qtsm_message_res *qtsm_res)
{
    if (qtsm_res) {
        switch (qtsm_res->type) {
            case DESCRIBE_PCR:
                if (qtsm_res->qtsm_message_res_u.describe_pcr.data) {
                    free(qtsm_res->qtsm_message_res_u.describe_pcr.data);
                    qtsm_res->qtsm_message_res_u.describe_pcr.data = NULL;
                }
                break;
            case EXTEND_PCR:
                if (qtsm_res->qtsm_message_res_u.extend_pcr.data) {
                    free(qtsm_res->qtsm_message_res_u.extend_pcr.data);
                    qtsm_res->qtsm_message_res_u.extend_pcr.data = NULL;
                }
                break;
            case ATTESTATION:
                if (qtsm_res->qtsm_message_res_u.attestation.data) {
                    free(qtsm_res->qtsm_message_res_u.attestation.data);
                    qtsm_res->qtsm_message_res_u.attestation.data = NULL;
                }
                break;
            default:
                break;
        }
    }
}

static int qtsm_describe_pcr_prepare(struct iovec *req,
    qtsm_message_req *qtsm_req, struct iovec *res, uint16_t index)
{
    int rc;

    res->iov_len = QTSM_RESPONSE_MAX_SIZE;

    rc = qtsm_message_alloc(res);
    if (rc != NO_ERROR)
        return rc;

    qtsm_req->type = DESCRIBE_PCR;
    qtsm_req->qtsm_message_req_u.describe_pcr.index = index;

    /* Transfer qtsm_req to iov base address of request buffer */
    rc = qtsm_encode_request_cbor(qtsm_req, (unsigned char **)&(req->iov_base),
        &(req->iov_len));

    return rc;
}

static int qtsm_describe_pcr_parse_response(const struct iovec *res,
    qtsm_message_res *qtsm_res, bool *locked, uint8_t *data, uint32_t *data_len)
{
    int rc;

    /* Get qtsm_res from iov base address of response buffer */
    rc = qtsm_decode_response_cbor((const char *)res->iov_base,
                                   res->iov_len, qtsm_res);
    if (rc != NO_ERROR)
        return rc;

    if (qtsm_res->type == ERROR_CODE)
        return qtsm_res->qtsm_message_res_u.error_num;

    if (qtsm_res->qtsm_message_res_u.describe_pcr.data_len > *data_len)
        return BUFFER_TOO_SMALL;

    *data_len = qtsm_res->qtsm_message_res_u.describe_pcr.data_len;
    *locked = qtsm_res->qtsm_message_res_u.describe_pcr.locked;
    memcpy(data, qtsm_res->qtsm_message_res_u.describe_pcr.data, *data_len);

    return rc;
}

int qtsm_describe_pcr(int fd, uint16_t index, bool *locked,
                      uint8_t *data, uint32_t *data_len)
{
    struct iovec req = {0};
    struct iovec res = {0};
    qtsm_message_req qtsm_req = {0};
    qtsm_message_res qtsm_res = {0};
    int rc;
    struct qtsm_message message;

    if (index >= QTSM_MAX_PCR_COUNT)
        return INVALID_INDEX;

    if ((*data_len == 0) || (!data) || !locked)
        return INVALID_ARGUMENT;

    rc = qtsm_describe_pcr_prepare(&req, &qtsm_req, &res, index);
    if (rc != NO_ERROR)
        goto error;

    if (req.iov_len > QTSM_REQUEST_MAX_SIZE) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    /* Conveter to qtsm_message */
    message.request = req;
    message.response = res;

    rc = qtsm_process_request(fd, &message);
    if (rc != NO_ERROR) {
        fprintf (stderr, "failed to process qtsm_describe_pcr request\n");
        rc = INVALID_RESPONSE;
        goto error;
    }

    rc = qtsm_describe_pcr_parse_response(&res, &qtsm_res, locked,
                                          data, data_len);

    qtsm_response_free(&qtsm_res);

error:
    qtsm_message_free(&req, &res);

    return rc;
}

static int qtsm_extend_pcr_prepare(struct iovec *req,
    qtsm_message_req *qtsm_req, struct iovec *res,
    uint16_t index, const uint8_t *req_data,
    uint32_t req_data_len)
{
    int rc;

    res->iov_len = QTSM_RESPONSE_MAX_SIZE;

    rc = qtsm_message_alloc(res);
    if (rc != NO_ERROR)
        return rc;

    qtsm_req->type = EXTEND_PCR;
    qtsm_req->qtsm_message_req_u.extend_pcr.index = index;
    qtsm_req->qtsm_message_req_u.extend_pcr.data = (uint8_t *)req_data;
    qtsm_req->qtsm_message_req_u.extend_pcr.data_len = req_data_len;

    /* Transfer qtsm_req to iov base address of request buffer */
    rc = qtsm_encode_request_cbor(qtsm_req, (unsigned char **)&(req->iov_base),
        &(req->iov_len));

    return rc;
}

static int qtsm_extend_pcr_parse_response(const struct iovec *res,
    qtsm_message_res *qtsm_res, uint8_t *pcr_data,
    uint32_t *pcr_data_len)
{
    int rc;

    /* Get qtsm_res from iov base address of response buffer */
    rc = qtsm_decode_response_cbor((const char *)res->iov_base, res->iov_len,
                                   qtsm_res);
    if (rc != NO_ERROR)
        return rc;

    if (qtsm_res->type == ERROR_CODE)
        return qtsm_res->qtsm_message_res_u.error_num;

    if (qtsm_res->qtsm_message_res_u.extend_pcr.data_len > *pcr_data_len)
        return BUFFER_TOO_SMALL;

    *pcr_data_len = qtsm_res->qtsm_message_res_u.extend_pcr.data_len;
    memcpy(pcr_data, qtsm_res->qtsm_message_res_u.extend_pcr.data,
           *pcr_data_len);

    return rc;
}

int qtsm_extend_pcr(int fd, uint16_t index, const uint8_t *req_data,
    uint32_t req_data_len, uint8_t *pcr_data,
    uint32_t *pcr_data_len)
{
    struct iovec req = {0};
    struct iovec res = {0};
    qtsm_message_req qtsm_req = {0};
    qtsm_message_res qtsm_res = {0};
    int rc;
    struct qtsm_message message;

    if ((req_data_len == 0) || !req_data)
        return INVALID_ARGUMENT;

    if (req_data_len > QTSM_PCR_MAX_LENGTH)
        return INPUT_TOO_LARGE;

    if (index >= QTSM_MAX_PCR_COUNT)
        return INVALID_INDEX;

    if ((*pcr_data_len == 0) || (!pcr_data))
        return INVALID_ARGUMENT;

    rc = qtsm_extend_pcr_prepare(&req, &qtsm_req, &res, index,
                                 req_data, req_data_len);
    if (rc != NO_ERROR)
        goto error;

    if (req.iov_len > QTSM_REQUEST_MAX_SIZE) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    /* Conveter to qtsm_message */
    message.request = req;
    message.response = res;

    rc = qtsm_process_request(fd, &message);
    if (rc != NO_ERROR) {
        fprintf (stderr, "failed to process qtsm_extend_pcr request\n");
        rc = INVALID_RESPONSE;
        goto error;
    }

    rc = qtsm_extend_pcr_parse_response(&res, &qtsm_res,
        pcr_data, pcr_data_len);

    qtsm_response_free(&qtsm_res);

error:
    qtsm_message_free(&req, &res);

    return rc;
}

static int qtsm_lock_pcr_prepare(struct iovec *req, qtsm_message_req *qtsm_req,
    struct iovec *res, uint16_t index)
{
    int rc;

    res->iov_len = QTSM_RESPONSE_MAX_SIZE;

    rc = qtsm_message_alloc(res);
    if (rc != NO_ERROR)
        return rc;

    qtsm_req->type = LOCK_PCR;
    qtsm_req->qtsm_message_req_u.lock_pcr.index = index;

    /* Transfer qtsm_req to iov base address of request buffer */
    rc = qtsm_encode_request_cbor(qtsm_req, (unsigned char **)&(req->iov_base),
        &(req->iov_len));

    return rc;
}

static int qtsm_lock_pcr_parse_response(const struct iovec *res,
    qtsm_message_res *qtsm_res, bool *locked)
{
    int rc;

    /* Get qtsm_res from iov base address of response buffer */
    rc = qtsm_decode_response_cbor((const char *)res->iov_base,
                                   res->iov_len, qtsm_res);
    if (rc != NO_ERROR)
        return rc;

    if (qtsm_res->type == ERROR_CODE)
        return qtsm_res->qtsm_message_res_u.error_num;

    *locked = qtsm_res->qtsm_message_res_u.lock_pcr.locked;

    return rc;
}

int qtsm_lock_pcr(int fd, uint16_t index, bool *locked)
{
    struct iovec req = {0};
    struct iovec res = {0};
    qtsm_message_req qtsm_req = {0};
    qtsm_message_res qtsm_res = {0};
    int rc;
    struct qtsm_message message;

    if (index >= QTSM_MAX_PCR_COUNT)
        return INVALID_INDEX;

    rc = qtsm_lock_pcr_prepare(&req, &qtsm_req, &res, index);
    if (rc != NO_ERROR)
        goto error;

    if (req.iov_len > QTSM_REQUEST_MAX_SIZE) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    /* Conveter to qtsm_message */
    message.request = req;
    message.response = res;

    rc = qtsm_process_request(fd, &message);
    if (rc != NO_ERROR) {
        fprintf (stderr, "failed to process qtsm_lock_pcr request\n");
        rc = INVALID_RESPONSE;
        goto error;
    }

    rc = qtsm_lock_pcr_parse_response(&res, &qtsm_res, locked);

    qtsm_response_free(&qtsm_res);

error:
    qtsm_message_free(&req, &res);

    return rc;
}

static int qtsm_lock_pcrs_prepare(struct iovec *req,
    qtsm_message_req *qtsm_req, struct iovec *res,
    uint32_t count, const uint16_t *range)
{
    int rc;

    res->iov_len = QTSM_RESPONSE_MAX_SIZE;

    rc = qtsm_message_alloc(res);
    if (rc != NO_ERROR)
        return rc;

    qtsm_req->type = LOCK_PCRS;
    qtsm_req->qtsm_message_req_u.lock_pcrs.count = count;
    qtsm_req->qtsm_message_req_u.lock_pcrs.range = (uint16_t *)range;

    /* Transfer qtsm_req to iov base address of request buffer */
    rc = qtsm_encode_request_cbor(qtsm_req,
        (unsigned char **)&(req->iov_base), &(req->iov_len));

    return rc;
}

static int qtsm_lock_pcrs_parse_response(const struct iovec *res,
    qtsm_message_res *qtsm_res, bool *locked)
{
    int rc;

    /* Get qtsm_res from iov base address of response buffer */
    rc = qtsm_decode_response_cbor((const char *)res->iov_base, res->iov_len,
                                   qtsm_res);
    if (rc != NO_ERROR)
        return rc;

    if (qtsm_res->type == ERROR_CODE)
        return qtsm_res->qtsm_message_res_u.error_num;

    *locked = qtsm_res->qtsm_message_res_u.lock_pcrs.locked;

    return rc;
}

int qtsm_lock_pcrs(int fd, uint32_t count, const uint16_t *range, bool *locked)
{
    struct iovec req = {0};
    struct iovec res = {0};
    qtsm_message_req qtsm_req = {0};
    qtsm_message_res qtsm_res = {0};
    int rc;
    struct qtsm_message message;

    if (count > QTSM_MAX_PCR_COUNT)
        return INPUT_TOO_LARGE;

    if ((count == 0) || (!range))
        return INVALID_ARGUMENT;

    rc = qtsm_lock_pcrs_prepare(&req, &qtsm_req, &res, count, range);
    if (rc != NO_ERROR)
        goto error;

    if (req.iov_len > QTSM_REQUEST_MAX_SIZE) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    /* Conveter to qtsm_message */
    message.request = req;
    message.response = res;

    rc = qtsm_process_request(fd, &message);
    if (rc != NO_ERROR) {
        fprintf (stderr, "failed to process qtsm_lock_pcrs request\n");
        rc = INVALID_RESPONSE;
        goto error;
    }

    rc = qtsm_lock_pcrs_parse_response(&res, &qtsm_res, locked);

    qtsm_response_free(&qtsm_res);

error:
    qtsm_message_free(&req, &res);

    return rc;
}

static int qtsm_get_describe_prepare(struct iovec *req,
    qtsm_message_req *qtsm_req, struct iovec *res)
{
    int rc;

    res->iov_len = QTSM_RESPONSE_MAX_SIZE;

    rc = qtsm_message_alloc(res);
    if (rc != NO_ERROR)
        return rc;

    qtsm_req->type = DESCRIBE_QTSM;

    /* Transfer qtsm_req to iov base address of request buffer */
    rc = qtsm_encode_request_cbor(qtsm_req,
        (unsigned char **)&(req->iov_base), &(req->iov_len));

    return rc;
}

static int qtsm_get_describe_parse_response(const struct iovec *res,
    qtsm_message_res *qtsm_res, describe_qtsm *qtsm_description)
{
    unsigned int i;
    int rc;
    struct describe_qtsm *cur_qtsm = NULL;

    /* Get qtsm_res from iov base address of response buffer */
    rc = qtsm_decode_response_cbor((const char *)res->iov_base, res->iov_len,
                                   qtsm_res);
    if (rc != NO_ERROR)
        return rc;

    if (qtsm_res->type == ERROR_CODE)
        return qtsm_res->qtsm_message_res_u.error_num;

    cur_qtsm = &(qtsm_res->qtsm_message_res_u.describe_qtsm);

    qtsm_description->version_major = cur_qtsm->version_major;
    qtsm_description->version_minor = cur_qtsm->version_minor;
    qtsm_description->version_patch = cur_qtsm->version_patch;

    memcpy(qtsm_description->module_id, cur_qtsm->module_id,
           QTSM_MODULE_ID_MAX_SIZE);

    qtsm_description->max_pcrs = cur_qtsm->max_pcrs;
    qtsm_description->digest = cur_qtsm->digest;

    qtsm_description->locked_pcrs_num = cur_qtsm->locked_pcrs_num;
    for (i = 0; i < cur_qtsm->locked_pcrs_num; i++)
        qtsm_description->locked_pcrs[i] = cur_qtsm->locked_pcrs[i];

    return rc;
}

int qtsm_get_describe(int fd, describe_qtsm *qtsm_description)
{
    struct iovec req = {0};
    struct iovec res = {0};
    qtsm_message_req qtsm_req = {0};
    qtsm_message_res qtsm_res = {0};
    int rc;
    struct qtsm_message message;

    if (!qtsm_description)
        return BUFFER_TOO_SMALL;

    rc = qtsm_get_describe_prepare(&req, &qtsm_req, &res);
    if (rc != NO_ERROR)
        goto error;

    if (req.iov_len > QTSM_REQUEST_MAX_SIZE) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    /* Conveter to qtsm_message */
    message.request = req;
    message.response = res;

    rc = qtsm_process_request(fd, &message);
    if (rc != NO_ERROR) {
        fprintf (stderr, "failed to process qtsm_get_describe request\n");
        rc = INVALID_RESPONSE;
        goto error;
    }

    rc = qtsm_get_describe_parse_response(&res, &qtsm_res, qtsm_description);

    qtsm_response_free(&qtsm_res);

error:
    qtsm_message_free(&req, &res);

    return rc;
}

static int qtsm_get_attestation_prepare(struct iovec *req,
    qtsm_message_req *qtsm_req, struct iovec *res,
    const uint8_t *user_data, uint32_t user_data_len,
    const uint8_t *nonce_data, uint32_t nonce_data_len,
    const uint8_t *pubkey_data, uint32_t pubkey_len)
{
    int rc;

    res->iov_len = QTSM_RESPONSE_MAX_SIZE;

    rc = qtsm_message_alloc(res);
    if (rc != NO_ERROR)
        return rc;

    qtsm_req->type = ATTESTATION;

    qtsm_req->qtsm_message_req_u.attestation.user_data_len = user_data_len;
    qtsm_req->qtsm_message_req_u.attestation.user_data = (uint8_t *)user_data;

    qtsm_req->qtsm_message_req_u.attestation.nonce_len = nonce_data_len;
    qtsm_req->qtsm_message_req_u.attestation.nonce = (uint8_t *)nonce_data;

    qtsm_req->qtsm_message_req_u.attestation.pubkey_len = pubkey_len;
    qtsm_req->qtsm_message_req_u.attestation.pubkey = (uint8_t *)pubkey_data;

    /* Transfer qtsm_req to iov base address of request buffer */
    rc = qtsm_encode_request_cbor(qtsm_req,
        (unsigned char **)&(req->iov_base), &(req->iov_len));

    return rc;
}

static int qtsm_get_attestation_parse_response(const struct iovec *res,
    qtsm_message_res *qtsm_res, uint8_t *att_doc_data,
    uint32_t *att_doc_data_len)
{
    int rc;

    /* Get qtsm_res from iov base address of response buffer */
    rc = qtsm_decode_response_cbor((const char *)res->iov_base, res->iov_len,
                                   qtsm_res);
    if (rc != NO_ERROR)
        return rc;

    if (qtsm_res->type == ERROR_CODE)
        return qtsm_res->qtsm_message_res_u.error_num;

    if (qtsm_res->qtsm_message_res_u.attestation.data_len > *att_doc_data_len)
        return BUFFER_TOO_SMALL;

    *att_doc_data_len = qtsm_res->qtsm_message_res_u.attestation.data_len;
    memcpy(att_doc_data, qtsm_res->qtsm_message_res_u.attestation.data,
           *att_doc_data_len);

    return rc;
}

int qtsm_get_attestation(int fd, const uint8_t *user_data,
    uint32_t user_data_len, const uint8_t *nonce_data,
    uint32_t nonce_data_len, const uint8_t *pubkey_data,
    uint32_t pubkey_len, uint8_t *att_doc_data,
    uint32_t *att_doc_data_len)
{
    struct iovec req = {0};
    struct iovec res = {0};
    qtsm_message_req qtsm_req = {0};
    qtsm_message_res qtsm_res = {0};
    struct qtsm_message message;
    int rc;

    if (!att_doc_data || *att_doc_data_len == 0)
        return BUFFER_TOO_SMALL;

    if (user_data_len > QTSM_USER_DATA_MAX_SIZE ||
        nonce_data_len > QTSM_NONCE_MAX_SIZE ||
        pubkey_len > QTSM_PUBLIC_KEY_MAX_SIZE)
        return INPUT_TOO_LARGE;

    if ((user_data_len && !user_data) ||
        (nonce_data_len && !nonce_data) ||
        (pubkey_len && !pubkey_data))
        return INVALID_ARGUMENT;

    rc = qtsm_get_attestation_prepare(&req, &qtsm_req, &res,
                                      user_data, user_data_len,
                                      nonce_data, nonce_data_len,
                                      pubkey_data, pubkey_len);
    if (rc != NO_ERROR)
        goto error;

    if (req.iov_len > QTSM_REQUEST_MAX_SIZE) {
        rc = INTERNAL_ERROR;
        goto error;
    }

    /* Conveter to qtsm_message */
    message.request = req;
    message.response = res;

    rc = qtsm_process_request(fd, &message);
    if (rc != NO_ERROR) {
        fprintf (stderr, "failed to process get_attestation_doc request\n");
        rc = INVALID_RESPONSE;
        goto error;
    }

    rc = qtsm_get_attestation_parse_response(&res, &qtsm_res,
        att_doc_data, att_doc_data_len);

    qtsm_response_free(&qtsm_res);

error:
    qtsm_message_free(&req, &res);
    return rc;
}

struct attestation_document* qtsm_new_doc(void)
{
    struct attestation_document *doc = malloc(sizeof(struct attestation_document));
    if (!doc)
        fprintf(stderr, "failed to alloca attestation_doc.\n");
    else
        memset(doc, 0, sizeof(struct attestation_document));

    return doc;
}

void qtsm_free_doc(struct attestation_document **doc)
{
    if (!(*doc))
        return;

    free(*doc);
    *doc = NULL;
}
